<?php
/**
 * Copyright (c) 2006, PORTALIER Julien
 * 
 * Licensed under The LGPL License
 * Redistributions of files must retain the above copyright notice.
 * 
 * @copyright    Copyright (c) 2006, Julien PORTALIER
 * @link         http://code.google.com/p/feather-cm
 * @license      http://www.opensource.org/licenses/lgpl-license.php The LGPL License
 */

class Member extends ActiveRecord
{
	protected $hasMany      = array('Group');
#	public    $defaultOrder = 'Member.created DESC';
	
	function login($id=null, $password=null)
	{
		// validation
		if (empty($id))
		{
			if (!empty($this->data['Member']['id']))
				$id = $this->data['Member']['id'];
			else
			{
				$this->validationErrors['Member']['id'] = _('You must enter your nickname.');
				$valid = false;
			}
		}
		
		if (empty($password))
		{
			if (!empty($this->data['Member']['password']))
				$password = $this->data['Member']['password'];
			else
			{
				$this->validationErrors['Member']['password'] = _('You must enter your password.');
				$valid = false;
			}
		}
		
		if (isset($valid) and !$valid)
			return false;
		
		$this->process = Q_PROCESS_LITE;
		$data = $this->read('id, password, salt, deleted_on', $id, -1);
		if (!empty($data))
		{
			# account exists
			list($salt, $c_password) = $this->cryptPwd($password, $data['salt']);
			if ($data['password'] == $c_password)
			{
				# identification succesful
				$this->Group->condition('member_id', $data['id']);
				$data['groups']   = $this->Group->listField('name', -1);
				$data['password'] = $password;
				
				# was account deleted? let's restore it!
				if (!empty($data['deleted_on']))
				{
					$this->id = $data['id'];
					$this->saveField('deleted_on', null);
					$this->id = null;
				}
				
				unset($data['salt']);
				unset($data['deleted_on']);
				return $data;
			}
			
			# identification problem
			$this->validationErrors['Member']['password'] = _('Wrong password.');
			return;
		}
		
		// problem
		$this->validationErrors['Member']['id'] = _('There is no such account.');
		return;
	}
	
	function cryptPwd($password, $salt=null)
	{
		if (is_null($salt))
			$salt = substr(md5(uniqid(mt_rand(), true)), 0, 5);
		$password = sha1($salt.sha1($password));
		return array($salt, $password);
	}
	
	//read
	
	function & getList($limit, $page=null)
	{
		$this->count = $this->findCount(null, false);
		$this->condition('Member.id', '<> Anonymous');
		return $this->findAll('Member.id, Member.created, Member.firstname, Member.lastname, Member.email, Member.country, Member.birthday', $limit, $page);
	}
	
	function & getListSearch($q, $limit, $page=null)
	{
		$this->conditions(array(
			'id'    => "LIKE %$q%",
			'email' => "LIKE %$q%",
			'firstname' => "LIKE %$q%",
			'lastname'  => "LIKE %$q%",
		));
		$this->_operator = 'OR';
		return $this->findAll('Member.id, Member.created, Member.firstname, Member.lastname, Member.email, Member.country, Member.birthday', $limit, $page);
	}
	
	function & search($q)
	{
		$this->conditions(array('id' => "LIKE $q%", 'deleted_on' => '-! IS NULL'));
		$this->process = Q_PROCESS_LIST;
		return $this->findAll('id', null, null, -1);
	}
	
	function & get($search, $fields='id, email')
	{
		$this->Member->conditions(array('email' => $search, 'id' => $search));
		$this->Member->operator('OR');
		return $this->Member->find($fields, -1);
	}
	
	// validates
	
	function beforeValidate()
	{
		if (empty($this->validate))
		{
			$this->validate = array(
				'id' => array(
					Valid::NotEmpty => _('A nickname is required to identify yourself on the site.'),
					'/^[_a-zA-Z0-9\-]{3,}$/' => _('Your nickname used invalid characters.'),
					'validateUnique' => _("This nickname isn't available.")
				),
				'password'     => array(
					Valid::NotEmpty => _('You must enter a password to protect your account.'),
					'/.{5,}/' => _('Your password must be at least be 5 characters long.')
				),
				'password2'    => array('validateRepeatPwd'  => _("Your passwords didn't match, please retry.")),
				'old_password' => array('validateCurrentPwd' => _('Wrong password.')),
				'email'   => array(
					Valid::Email     => _('A valid email is needed to contact you when needed.'),
					'validateUnique' => _('There already is an account for that email.'),
				),
				'user_agreement' => array(Valid::NotEmpty => _('You must agree to the Terms of Use.'))
			);
		}
		return true;
	}
	
	function validateRepeatPwd()
	{
		if ((empty($this->id) or isset($this->data['Member']['old_password'])) and
			$this->data['Member']['password'] != $this->data['Member']['password2'])
		{
			$this->data['Member']['password']  = '';
			$this->data['Member']['password2'] = '';
			return true;
		}
		return false;
	}
	
	function validateCurrentPwd()
	{
		$salt = $this->field('salt');
		list($salt, $password) = $this->cryptPwd($this->data['Member']['old_password'], $salt);
		
		$this->conditions(array('id' => $this->id, 'password' => $password));
		$this->recursive = -1;
		$res = $this->find('id');
		
		// wrong password?
		if (empty($res))
		{
			$this->data['Member']['old_password'] = '';
			$this->data['Member']['password']     = '';
			$this->data['Member']['password2']    = '';
			return true;
		}
		
		return false;
	}
	
	// create & update
	
	function register($id, $groups)
	{
		$this->setBoolean('user_agreement');
		$this->id = null;
		
		$this->db->transactions('start');
		if ($this->save(null, true, 'insert'))
		{
			$Acl = new Acl();
			$Acl->registerAro("User.$id");
			$this->Group->register($id, &$groups);
			
			// account created!
			$this->db->transactions('commit');
			return true;
		}
		
		// error?
		$this->db->transactions('rollback');
		return false;
	}
	
	function beforeSave()
	{
		if (isset($this->data['Member']['password']))
		{
			list($salt, $password) = $this->cryptPwd($this->data['Member']['password']);
			$this->data['Member']['password'] = $password;
			$this->data['Member']['salt']     = $salt;
		}
		
		if (isset($this->data['Member']['about']))
		{
			Load::libs('markdown_syntax');
			$MarkdownSyntax = new MarkdownSyntax;
			$this->data['Member']['about_html'] = $MarkdownSyntax->transform($this->data['Member']['about']);
		}
		
		#$this->setBoolean('allow_contact');
		return true;
	}
	
	
	function has_invitations_left($member_id)
	{
		$this->condition("Member.id", $member_id);
		$nb_invitations = $this->field('invitations', -1);
		return ($nb_invitations > 0 or $nb_invitations == -1);
	}
	
	function decrement_invitations_counter($member_id)
	{
		$this->condition("Member.id", $member_id);
		$nb_invitations = $this->field('invitations', -1);
		
		if ($nb_invitations > 0)
		{
			$this->id = $member_id;
			$this->saveField('invitations', '-! invitations - 1');
		}
	}
	
	
	protected function before_delete()
	{
		if (!$this->exists($this->id)) {
			throw new Exception("No such member: {$member_id}", 404);
		}
		return true;
	}
	
	/**
	 * Doesn't actually delete the entry from the database, but marks the account
	 * as deleted ; deletes some personal informations ; destroys orphaned webcomics ;
	 * and eventually anonymizes comments throughout the website.
   */
	function delete($id)
	{
		$this->delete_orphaned_webcomics($id);
		$this->anonymize_comments($id);
		$this->delete_informations($id);
		return true;
	}
	
	# Deletes her webcomics (where she is the sole authors only).
	private function delete_orphaned_webcomics($id)
	{
		$webcomics = $this->db->select("SELECT webcomics.id, webcomics.domain, webcomics.title ".
			"FROM webcomics ".
			"INNER JOIN authors ON authors.webcomic_id = webcomics.id ".
			"WHERE authors.member_id = ".$this->db->value($id)." ;",
			Q_PROCESS_LITE);
		
		$W = $this->load->model('Webcomic');
		
		foreach($webcomics as $i => $webcomic)
		{
			$authors = $this->db->select("SELECT member_id FROM authors ".
				"WHERE webcomic_id = {$webcomic['id']} ;");
			
			if (count($authors) == 1) {
				$W->delete($webcomic['id'], $webcomic['domain']);
			}
		}
	}
	
	private function anonymize_comments($id)
	{
		$where = array('member_id' => $id);
		$data  = array(
			'member_id' => 'Anonymous',
			'user_name' => $id,
		);
		
		$tables = array('blog_couloir_comments', 'blog_news_comments',
			'blog_interviews_comments', 'forum_comments', 'forum_topics');
		foreach ($tables as $table) {
			$this->db->update($table, $where, $data);
		}
		
		$webcomics = $this->db->select("SELECT domain FROM webcomics ;", Q_PROCESS_LIST);
		foreach($webcomics as $domain)
		{
			$table = str_replace('.', '-', $domain).'.comments';
			$this->db->update($table, $where, $data);
		}
	}
	
	private function delete_informations($id)
	{
		Load::libs('avatar');
		$Avatar = MemberAvatar::instance();
		$Avatar->delete($id);
		
		$updates = array(
			'modified'      => null,
			'about'         => null,
			'about_html'    => null,
			'firstname'     => null,
			'lastname'      => null,
			'birthday'      => null,
			'country'       => null,
			'town'          => null,
			'website'       => null,
			'allow_contact' => false,
			'deleted_on'    => substr(Time::SQL(), 0, 10),
		);
		$where = array('id' => $id);
		
		$this->db->update('members', $where, $updates);
	}
}
?>
